/*
 * Copyright (c) 2024 彭友聪
 * FFmpegDemo is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * IDE: CLion
 * Project:  FFmpegDemo
 * File:     video_play_demo.cpp
 * @author:  御承扬
 * @Email: 2923616405@qq.com
 * @Date:    2024/11/23
 * @Time:    14:33
 * 
 * */

//
// Created by pengyoucong on 2024/11/23.
//

#include "video_play_demo.h"
#include <iostream>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
#include <SDL2/SDL.h>

void play_MP4(const char *file_path) {
    AVFormatContext *format_ctx = nullptr;
    unsigned int video_stream_index = 0;
    AVCodecContext *codec_ctx = nullptr;
    const AVCodec *codec;
    AVPacket packet;
    AVFrame *frame = nullptr;
    AVFrame *rgb_frame = nullptr;
    struct SwsContext *sws_ctx = nullptr;
    SDL_Window *window = nullptr;
    SDL_Renderer *renderer = nullptr;
    SDL_Texture *texture = nullptr;
    SDL_Rect rect;
    int frame_number = 0;

    // 初始化 FFmpeg
    avformat_network_init();

    // 打开输入文件
    if (avformat_open_input(&format_ctx, file_path, nullptr, nullptr) != 0) {
        std::cerr << "Could not open input file." << std::endl;
        goto end;
    }

    // 查找流信息
    if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
        std::cerr << "Could not find stream information." << std::endl;
        goto end;
    }

    // 查找视频流
    for (unsigned i = 0; i < format_ctx->nb_streams; ++i) {
        if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }

    if (video_stream_index == -1) {
        std::cerr << "Could not find video stream." << std::endl;
        goto end;
    }

    codec = avcodec_find_decoder(format_ctx->streams[video_stream_index]->codecpar->codec_id);
    if (!codec) {
        std::cerr << "Could not find decoder." << std::endl;
        goto end;
    }

    codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        std::cerr << "Could not allocate codec context." << std::endl;
        goto end;
    }

    if (avcodec_parameters_to_context(codec_ctx, format_ctx->streams[video_stream_index]->codecpar) < 0) {
        std::cerr << "Could not copy codec parameters to context." << std::endl;
        goto end;
    }

    if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
        std::cerr << "Could not open codec." << std::endl;
        goto end;
    }

    // 分配包和帧
    frame = av_frame_alloc();
    rgb_frame = av_frame_alloc();
    if (!frame || !rgb_frame) {
        std::cerr << "Could not allocate frame." << std::endl;
        goto end;
    }

    // 初始化 SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        std::cerr << "Could not initialize SDL: " << SDL_GetError() << std::endl;
        goto end;
    }

    window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                              codec_ctx->width, codec_ctx->height, SDL_WINDOW_SHOWN);
    if (!window) {
        std::cerr << "Could not create SDL window: " << SDL_GetError() << std::endl;
        goto end;
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (!renderer) {
        std::cerr << "Could not create SDL renderer: " << SDL_GetError() << std::endl;
        goto end;
    }

    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING,
                                codec_ctx->width, codec_ctx->height);
    if (!texture) {
        std::cerr << "Could not create SDL texture: " << SDL_GetError() << std::endl;
        goto end;
    }

    // 初始化图像转换上下文
    sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
                             codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24,
                             SWS_BILINEAR, nullptr, nullptr, nullptr);
    if (!sws_ctx) {
        std::cerr << "Could not initialize the conversion context." << std::endl;
        goto end;
    }

    // 读取帧
    while (av_read_frame(format_ctx, &packet) >= 0) {
        if (packet.stream_index == video_stream_index) {
            if (avcodec_send_packet(codec_ctx, &packet) < 0) {
                av_packet_unref(&packet);
                continue;
            }

            while (avcodec_receive_frame(codec_ctx, frame) == 0) {
                frame_number++;

                // 转换帧格式
                if (sws_scale(sws_ctx, frame->data, frame->linesize, 0, codec_ctx->height,
                              rgb_frame->data, rgb_frame->linesize) < 0) {
                    std::cerr << "Could not convert frame." << std::endl;
                    continue;
                }

                // 更新纹理
                SDL_UpdateTexture(texture, &rect, rgb_frame->data[0], rgb_frame->linesize[0]);

                // 渲染帧
                SDL_RenderClear(renderer);
                SDL_RenderCopy(renderer, texture, nullptr, nullptr);
                SDL_RenderPresent(renderer);

                // 延迟一段时间以模拟帧率
                SDL_Delay(1000 / 25); // 假设帧率为25fps
            }
            av_packet_unref(&packet);
        }
    }

end:
    // 释放资源
    if (sws_ctx) sws_freeContext(sws_ctx);
    if (texture) SDL_DestroyTexture(texture);
    if (renderer) SDL_DestroyRenderer(renderer);
    if (window) SDL_DestroyWindow(window);
    SDL_Quit();
    av_frame_free(&rgb_frame);
    av_frame_free(&frame);
    avcodec_free_context(&codec_ctx);
    avformat_close_input(&format_ctx);
}

